#pragma once

#include <QtCore/QString>

#include <boostext/smart_ptr.hpp>
#include <boostext/signal.hpp>
#include <engine.core/math.hpp>
#include <engine.core/Device.hpp>

#include "exports.hpp"
#include "Key.hpp"




namespace Engine
{
	namespace Input
	{
		/**
		 * InputDevice defines the interface for an Input Device.
		 * Such a device must handle mouse-, keyboard and input
		 * from other devices (like a joystick or a gamepad).
		 *
		 * It offers 2 signals that are emitted whenever a key has been pressed
		 * or released, and functions to query the key's states.
		 */
		class ENGINE_INPUT_API Device
			: public Engine::Device
		{
		public:

			friend class EngineImpl;
			typedef boost::shared_ptr<Device>                   ptr;
			typedef boost::signals2::signal<void (Key::Type)>   KeySignal;

		protected:

			KeySignal   m_pressed;
			KeySignal   m_clicked;

		public:

			/// The pressed signal is emitted whenever a button has been pressed down
			boostext::connector<KeySignal>   pressed;

			/// The clicked signal is emitted whenever a button has been released (ie. clicked)
			boostext::connector<KeySignal>   clicked;

		public:

			Device();

			/**
			 * Reports the name of the Device.
			 */
			QString name() const;



			/**
			 * Get the current mouse position in pixels, relative to
			 * the upper left corner.
			 */
			virtual int2 mousePositionPixels() const = 0;

			/**
			* Get the change of the mouse position since the last frame
			* in pixels.
			*/
			virtual int2 mouseDeltaPixels() const = 0;



			/**
			 * Get the current mouse position, independent of the window size.
			 * Whereas mousePosition() returns the position in pixels, this
			 * one returns a floating point number, describing the mouses' position
			 * in the window.
			 * This position is in the same space, as any point, transformed
			 * by the view projection matrix, that is, in screen space.
			 *
			 * [-1,  1]: upper left corner
			 * [ 1,  1]: upper right corner
			 * [ 1, -1]: lower right corner
			 * [-1, -1]: lower left corner
			 */
			virtual float2 mousePositionRelative() const = 0;

			/**
			 * Get the change of the mouse position since the last frame in
			 * relative screen coordinates.
			 */
			virtual float2 mouseDeltaRelative() const = 0;



			/**
			 * Get the change of the mouse wheel since the last frame.
			 */
			virtual float mouseWheelDelta() const = 0;



			/**
			 * Get the complete state of a key, that is up/down and pressed.
			 */
			virtual const Key& key(Key::Type k) const = 0;

			/**
			 * Test if a key has been pressed
			 * A press is an event that occurs when down switches from false to true
			 */
			bool keyPressed(Key::Type k) const;

			/**
			 * Test if a key has been clicked.
			 * A click is an event that occurs when down switches from true to false
			 */
			bool keyClicked(Key::Type k) const;

			/**
			 * Test if a key is currently down or up.
			 */
			bool keyDown(Key::Type k) const;



			/**
			 * Create an InputDevice of the specified type.
			 */
			static ptr create(const EngineInfo& info);

		private:

			/**
			 * Update the size of the window.
			 * The input class needs this to calculate
			 * the relative mouse position.
			 */
			virtual void resize(int width, int height) = 0;

			/**
			 * Change the window handle.
			 */
			virtual void changeWindow(HWND window) = 0;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
